"""
Created on 30-Jul-2009

@author: dexen
"""

from Queue import Queue

from dexen.system.base import task as tk
from dexen.system.node.master import ind_mgr as im


"""
class Queue(object):
    def __init__(self, size=10000):
        self.data = []
        self.max_size = size
        self.front = 0
        self.back = 0
        
    def put(self, item):
        if self.back == len(self.data):
            self.data.append(item)
        else:
            assert self.back < len(self.data)
            self.data[self.back] = item
        self.back += 1;
        if self.back == self.max_size:
            self.back = 0

    def get(self):
        if self.back == self.front:
            return None
        result = self.data[self.front]
        self.front += 1
        if self.front == self.max_size:
            self.front = 0
        return result
    
    def get_size(self):
        return abs(self.front - self.back)
""" 
    
class TaskQMgr(object):
    """Class
    A class for managing the assignment of partially-processed individuals
    to queues.
    
    """
    def __init__(self, tasks):
        """Constructor
        
        Args:
        queues : A dict that stores the queues of type multiprocessing.Queue.
        Each queue is referenced by the name of a task to which it belongs.
        
        tasks : A dict that stores all of the tasks properties set in
        the config.py file. Each task info -of type dict- is referenced by 
        its name.
        
        ind_mgr : Refers to a IndMgr object. It is used to get and set 
        actual data in the in-memory database (inds_db)  
        
        """
        
        """ Setting up queues """
        self.queues = {}
        self.tasks = tasks
        
        for name, task in tasks.items():
            assert isinstance(task, tk.TaskInfo)
            assert task.type == tk.TaskType
            self.queues[name] = Queue()
    
        print "taskq_mgr->init(), queues, ", self.queues 
        self.ind_mgr = im.IndMgr(self.tasks)
        
    def get_inds(self):
        """Get individuals from the first queue found to have enough inds.
        
        Returns a tuple consisting of two elements. The first element refers to
        the name of the queue from which the individuals were pulled. The second 
        holds the list of individuals to be sent off to a slave.
        
        """
        
        #TODO: sort the queues based on the current max_size for fairness
        for task_name in self.tasks.keys():
            queue = self.queues[task_name]
            assert isinstance(queue, Queue)
            task = self.tasks[task_name] 
            cnt = task.input_size
            if cnt is None: cnt = 1
            ids = []
            if queue.qsize() >= cnt:
                for _ in range(cnt):
                    ids.append(queue.get())
                inds = self.ind_mgr.get_inds(task_name, ids)
                return task_name, inds
        return None, []
    
    
    def set_inds(self, task_name, inds):
        """Put the individuals into the in-memory database (ind_db).
        
        After updating and inserting individuals in ind_db. It will place the 
        individuals into respective queues. The placement is determined by 
        ind_mgr in the form of queue_name and list of ids for it.
    
        Returns None for the moment.
        """
        res = self.ind_mgr.set_inds(inds)
        for task_name, ids in res.items():
            for id in ids:
                self.queues[task_name].put(id)            
        return None
    
    def recover_inds(self, task_name, inds):
        print "I am recovering inds for task: ", task_name
        for ind in inds:
            self.queues[task_name].put(ind.__id__)
        
    def __repr__(self):
        res = "Queue State\n"
        for task_name, item in self.queues.items():
            assert isinstance(item, Queue)
            res += "%s: %s\n" %(task_name, item.qsize()) 
        return res
